//implement Scanner

#include "Scanner.h"


TINYScanner::TINYScanner() : m_chIO(0), m_currentToken(0), m_currentState(0)
{
}

TINYScanner::TINYScanner(GIOStream *io) : m_chIO(io), m_currentToken(0), m_currentState(0)
{
}

TINYScanner::~TINYScanner()
{
}

TINYToken* TINYScanner::getToken()
{
    m_currentToken = new TINYToken;
    scan();
    return m_currentToken;
}

void TINYScanner::scan()
{
    m_currentState = &TINYScanStartState::instance();
    while (m_currentState)
    {
        if (getCharactor(m_ch))
            m_currentState->scan(this);
    }
}

bool TINYScanner::getCharactor(char &ch)
{
    m_chIO->getCharactor(ch);
}

void TINYScanner::changeState(TINYScanState *state)
{
    m_currentState = state;
}

/*****************************begin state pattern classes**************************/

TINYScanner::TINYScanState::TINYScanState()
{
}

TINYScanner::TINYScanState::~TINYScanState()
{
}

void TINYScanner::TINYScanState::changeState(TINYScanner *scanner, TINYScanState *state)
{
    if (state != this)
        scanner->changeState(state);
}

//--------------------------implement start state--------------------------------

TINYScanner::TINYScanStartState& TINYScanner::TINYScanStartState::_startState;

TINYScanner::TINYScanStartState::TINYScanStartState()
{
}

TINYScanner::TINYScanStartState::~TINYScanStartState()
{
}

void TINYScanner::TINYScanStartState::scan(TINYScanner *scanner)
{
    const TINYSpecialSymTable& specialTymTable = TINYSpecialSymTable::instance();
    TINYToken::TOKENTYPE tokenType;
    if (isspace(scanner->m_ch))
        return;
    else if (isdigit(scanner->m_ch))
    {
        scanner->m_currentToken->addCharactor(scanner->m_ch);
        changeState(scanner, &(TINYInNumberState::instance()));
    }
    else if (isalpha(scanner->m_ch))
    {
        scanner->m_currentToken->addCharactor(scanner->m_ch);
    }
    else if (scanner->m_ch == ':')
        changeState(scanner, &(TINYInAssignState::instance()));
    else if (scanner->m_ch == '(')
        changeState(scanner, &(TINYScanner::TINYInCommentState::instance()));
    else if ((tokenType = specialTymTable.getTokenType(scanner->m_ch)) != TINYToken::NONE)
    {
        scanner->m_currentToken->setTokenType(tokenType);
        scanner->m_currentToken->addCharactor(scanner->m_ch);
        changeState(scanner, 0);
    }
    else if (scanner->m_ch == EOF)
    {
        scanner->m_currentToken->setTokenType(TINYToken::ENDFILE);
        changeState(scanner, 0);
    }
    else
        changeState(scanner, 0);
}

//-------------------------implement incomment state---------------------

TINYScanner::TINYInCommentState TINYScanner::TINYInCommentState::_commentState;

TINYScanner::TINYInCommentState::TINYInCommentState()
{
}

TINYScanner::TINYInCommentState::~TINYInCommentState()
{
}

void TINYScanner::TINYInCommentState::scan(TINYScanner *scanner)
{
    if (scanner->m_ch == ')')
        changeState(scanner, TINYScanner::TINYScanStartState::instance());
    else if (scanner->m_ch == EOF)
    {
        scanner->m_currentToken->setTokenType(TINYToken::ENDFILE);
        changeState(scanner, 0);
    }
}


//-------------------------implement inassign state-------------------------

TINYScanner::TINYInAssignState TINYScanner::TINYInAssignState::_assignState;

TINYScanner::TINYInAssignState::TINYInAssignState()
{
}

TINYScanner::TINYInAssignState::~TINYInAssignState()
{
}

void TINYScanner::TINYInAssignState::scan(TINYScanner *scanner)
{
    if (scanner->m_ch == '=')
        scanner->m_currentToken->setTokenType(TINYToken::ASSIGN);
    else
    {
        scanner->ungetCharactor(scanner->m_ch);
        scanner->m_currentToken->setTokenType(TINYToken::ERROR);
    }
    changeState(scanner, 0);
}

//------------------------implement innumber state---------------------------

TINYScanner::TINYInNumberState TINYScanner::TINYInNumberState::_numberState;

TINYScanner::TINYInNumberState::TINYInNumberState()
{
}

TINYScanner::TINYInNumberState::~TINYInNumberState()
{
}

void TINYScanner::TINYInNumberState::scan(TINYScanner *scanner)
{
    if (!isdigit(scanner->m_ch))
    {
        scanner->ungetCharactor(scanner->m_ch);
        scanner->m_currentToken->setTokenType(TINYToken::NUM);
        changeState(scanner, 0);
    }
    else
        scanner->m_currentToken->addCharactor(scanner->m_ch);
}
